﻿using MyX.Message;
using MyX.Store;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MyX.Core
{
    /// <summary>
    /// 事件源对象
    /// </summary>
    public abstract class XEntity : XIEventSource
    {
        /// <summary>
        /// 默认构造函数
        /// </summary>
        public XEntity()
        {
            _observeres = new List<XIEntityObserver>();
            _observeres.Add(XClient.SaveObserver);
            _observeres.Add(XClient.SendObserver);

            _lock = new SemaphoreSlim(1);

            ID = Guid.NewGuid();

            Version = 0;
        }

        internal void AddObserver(XIEntityObserver observer)
        {
            _observeres.Add(observer);
        }

        /// <summary>
        /// 从仓储中加载实体事件
        /// <para>返回值表示实体从数据源中向前更新了多少次事件</para>
        /// </summary>
        /// <returns>实体版本更新数</returns>
        internal int Load(IList<XEvent> events)
        {
            try
            {
                _lock.Wait();

                var doCount = 0;
                foreach (var e in events)
                {
                    if (e.XSenderID != ID)
                        throw new Exception(string.Format("加载事件时，事件源ID错误。{0}，{1}", ID, e.XSenderID));

                    //如果事件的版本小于实体版本，则跳过
                    if (e.Version <= Version)
                        continue;

                    //如果取出的事件不是实体下一个版本事件，则事件版本不对
                    if (e.Version != (Version + 1))
                        throw new Exception(string.Format("实体ID为{0} ，当前实体版本为：{1} ，待加载事件版本为：{2}", ID, Version, e.Version));

                    Apply(e);

                    doCount += 1;
                }
                return doCount;
            }
            catch (Exception)
            {

                throw;
            }
            finally
            {
                _lock.Release();
            }
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <param name="e"></param>
        protected void Update(XEvent e)
        {
            try
            {
                _lock.Wait();

                Commit(e);
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                _lock.Release();
            }
        }

        private void Commit(XEvent e)
        {
            e.XSenderID = ID;
            e.Version = Version + 1;

            Apply(e);

            foreach (var observer in _observeres)
            {
                observer.EventHappend(e);
            }  
        }

        private void Apply(XEvent e)
        {
            dynamic self = this;
            self.Do((dynamic)e);

            Version += 1;
        }

        public Guid ID
        {
            get;
            internal set;
        }

        public long Version
        {
            get;
            internal set;
        }

        private List<XIEntityObserver> _observeres;
        private SemaphoreSlim _lock;
    }
}
